package edu.mse.camel.client.serverlib;

import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.io.File;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.FileReader;


import javax.naming.InitialContext;

import org.eclipse.jface.preference.IPreferenceStore;

import camel.client.BriefSessionInfo;
import camel.client.CamelRemote;
import camel.client.ClientEvent;
import camel.client.ClientEventChat;
import camel.client.ClientEventGenericPeer;
import camel.client.ClientEventInvite;
import camel.client.ClientEventSessionParticipantChange;
import camel.client.ClientEventTeamStatusChange;
import camel.client.CommandPack;
import camel.client.SessionTicket;
import edu.mse.camel.client.eventbus.pubsub.EventManager;
import edu.mse.camel.client.eventbus.pubsub.ISubscriber;
import edu.mse.camel.client.events.EventIDs;
import edu.mse.camel.client.events.IEvent;
import edu.mse.camel.client.events.peer.ReceiveChatEvent;
import edu.mse.camel.client.events.peer.ReceiveGenericPeerEvent;
import edu.mse.camel.client.events.peer.ReceiveInvitationEvent;
import edu.mse.camel.client.events.peer.SendChatEvent;
import edu.mse.camel.client.events.peer.SendGenericPeerEvent;
import edu.mse.camel.client.events.peer.SendInvitationEvent;
import edu.mse.camel.client.events.server.AssignRoleEvent;
import edu.mse.camel.client.events.server.CreateSessionEvent;
import edu.mse.camel.client.events.server.JoinSessionEvent;
import edu.mse.camel.client.events.server.LeaveSessionEvent;
import edu.mse.camel.client.events.server.LoginEvent;
import edu.mse.camel.client.events.server.LogoutEvent;
import edu.mse.camel.client.events.server.NotifySessionParticipantChange;
import edu.mse.camel.client.events.server.NotifySessionRoleChange;
import edu.mse.camel.client.events.server.NotifySyncFinished;
import edu.mse.camel.client.events.server.NotifyTeamStatusChange;
import edu.mse.camel.client.events.server.QueryLoginStatus;
import edu.mse.camel.client.events.server.QuerySessionListEvent;
import edu.mse.camel.client.events.server.SemaphoreEvent;
import edu.mse.camel.client.serverlib.internal.ClientEventConvertor;
import edu.mse.camel.client.serverlib.internal.ICamelServerPolling;
import edu.mse.camel.client.serverlib.internal.QueryThread;
import edu.mse.camel.client.session.team.QuerySessionParticipantStatus;
import edu.mse.camel.client.session.team.QueryTeamStatus;
import edu.mse.camel.client.preference.CamelServerPage;
import edu.mse.camel.client.log.Debug;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;


public class ServerLibEventHandler implements ISubscriber, ICamelServerPolling {
	
	private final String handlerName = "ServerLib EventHandler";
	
	private String  ipAddress;
	private Properties properties;
	private InitialContext ic;
	private CamelRemote camel;
	private SessionManagement sm = new SessionManagement();
	private SessionTicket ticket;
	private QueryThread thread;
	private CommandPack events;
	
	private boolean syncFinished;
	
	public void stop () {
		thread.abort();
		try {
			Debug.log_info(Activator.PLUGIN_ID, "ServerLibEventHandler : waiting for the query thread to stop");
			thread.join();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public ServerLibEventHandler () {
		createInitContext ();
		
		events = new CommandPack(null);

		thread = new QueryThread(this);
		thread.start();
	}
	
	// the initial context should only be created once
	private void createInitContext () {
	
		Properties props = new Properties();
		props.put(InitialContext.INITIAL_CONTEXT_FACTORY,
		"org.jnp.interfaces.NamingContextFactory");
		 props.put(InitialContext.PROVIDER_URL, "jnp://" + getIpAddress() + ":1099");
		//props.put(InitialContext.PROVIDER_URL, "jnp://buzzers.webhop.net:1099");
		try {
			Debug.log_info(Activator.PLUGIN_ID,  "Create init context");
			ic = new InitialContext(props);
			
			Debug.log_info(Activator.PLUGIN_ID,  "Start finding");
			camel = (CamelRemote) ic.lookup("Camel");
			
			if (camel == null) {
				Debug.log_error(Activator.PLUGIN_ID,  "Cannot find the camel");
			}
			else {
				Debug.log_info(Activator.PLUGIN_ID,  "Camel server found");
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	public String getIpAddress()  {
		IPreferenceStore store = Activator.getDefault().getPreferenceStore();
		ipAddress = store.getString(CamelServerPage.IP);
		return ipAddress;	
	}
//    public String getProviderURL()  {
//       	     		
// 		try {
// 			if (!new File(providerFile).exists()) {
// 	            System.out.println("Could not find ");
// 			}
// 			else  {
// 				if (ipAddress == null)  {
// 				BufferedReader in = new BufferedReader(new FileReader(providerFile));
// 				ipAddress = in.readLine();	
// 				System.out.println("IPADDRESS "  + ipAddress);
// 				}
// 			}
//	 	} 
// 		catch (FileNotFoundException e) {e.printStackTrace();}
// 		catch (IOException e1) {	e1.printStackTrace();}
// 		return ipAddress;
//    }


	// this method has to be synchronized to protect the use of the session ticket and event queue
	@Override
	public synchronized void notifyEvent(IEvent e) {
		//Debug.log_info(Activator.PLUGIN_ID, handlerName + ": enter notifyEvent");
		
		if ( e.getEventID().equalsIgnoreCase(EventIDs.LOGIN_EVENT) ) {
			Debug.log_info(Activator.PLUGIN_ID, handlerName + ": login event : " + e);
			ticket = sm.login ( camel, (LoginEvent)e);			
			thread.resumePolling();
		}
		else if (e.getEventID().equalsIgnoreCase(EventIDs.CREATESESSION_EVENT)) {
			Debug.log_info(Activator.PLUGIN_ID, handlerName + ": createSession event : " + e);
			ticket = sm.createSession(ticket, camel, (CreateSessionEvent)e);
			thread.resumePolling();
			
			thread.resumePolling();
		}
		else if (e.getEventID().equalsIgnoreCase(EventIDs.JOINSESSION_EVENT)) {
			
			Debug.log_info(Activator.PLUGIN_ID, handlerName + ": joinSession event : " + e);
			ticket = sm.joinSession(ticket, camel, (JoinSessionEvent) e);
			if (ticket.isLastOpSuccessful()) {
				Debug.log_info(Activator.PLUGIN_ID, handlerName + ": join session succedded");
				Debug.log_info(Activator.PLUGIN_ID, handlerName + ": sync started");
				syncFinished = false;
			}
			thread.resumePolling();
		}
		else if (e.getEventID().equalsIgnoreCase(EventIDs.QUERY_SESSION_LIST_EVENT)) {
			Debug.log_info(Activator.PLUGIN_ID, handlerName + ": sessionlist event : " + e);
			sm.getSessionList(ticket, camel,(QuerySessionListEvent)e);
		}
		else if (e.getEventID().equalsIgnoreCase(EventIDs.LOGOUT_EVENT)) {
			Debug.log_info(Activator.PLUGIN_ID, handlerName + ": sessionlist event : " + e);
			sm.logout(ticket, camel, (LogoutEvent)e);
			ticket = null;
		}
		else if (e.getEventID().equalsIgnoreCase(EventIDs.QUERY_SESSION_PARTICIPANT_STATUS_EVENT)) {
			Debug.log_info(Activator.PLUGIN_ID, handlerName + ": session participant status event : " + e);
			sm.querySessionParticipantStatus(ticket, camel, (QuerySessionParticipantStatus)e);
		}
		else if (e.getEventID().equalsIgnoreCase(EventIDs.QUERY_LOGIN_STATUS_EVENT)) {
			Debug.log_info(Activator.PLUGIN_ID, handlerName + ": query login status event : " + e);
			sm.QueryLoginStatus(ticket, camel, (QueryLoginStatus)e);
			thread.resumePolling();
		}
		else if (e.getEventID().equalsIgnoreCase(EventIDs.SEND_CHAT_EVENT)) {
			events.addCommand(ClientEventConvertor.convertFromSendChatEvent((SendChatEvent)e));
			thread.PollImmediately();
		}
		else if (e.getEventID().equalsIgnoreCase(EventIDs.QUERY_TEAM_STATUS_EVENT)) {
			Debug.log_info(Activator.PLUGIN_ID, handlerName + ": team list status event : " + e);
			sm.QueryTeamStatus(ticket, camel,(QueryTeamStatus)e);
		}
		else if (e.getEventID().equalsIgnoreCase(EventIDs.ASSIGN_ROLE_EVENT)) {
			Debug.log_info(Activator.PLUGIN_ID, handlerName + ": assign role status event : " + e);
			sm.assignRole(ticket, camel,(AssignRoleEvent)e);
			if(ticket.isLastOpSuccessful()) {
				Debug.log_info(Activator.PLUGIN_ID, handlerName + ": assign role succedded");
			}
		}
		else if (e.getEventID().equalsIgnoreCase(EventIDs.LEAVE_SESSION_EVENT)) {
			Debug.log_info(Activator.PLUGIN_ID, handlerName + ": leave session event : " + e);
		    sm.leaveSession(ticket, camel,(LeaveSessionEvent)e);
			if(ticket.isLastOpSuccessful()) {
				Debug.log_info(Activator.PLUGIN_ID, handlerName + ": leave session succedded");
			}
		}
		else if (e.getEventID().equalsIgnoreCase(EventIDs.SEND_INVITE_EVENT)) {
			Debug.log_info(Activator.PLUGIN_ID, handlerName + ": invite event : " + e);
			sm.invite(ticket, camel, (SendInvitationEvent)e);
		}
		else if (e.getEventID().equalsIgnoreCase(EventIDs.SEND_PEER_EVENT)) {
			events.addCommand(ClientEventConvertor.convertFromSendGenericPeerEvent((SendGenericPeerEvent)e));
			thread.PollImmediately();
		}
		else if (e.getEventID().equalsIgnoreCase(EventIDs.SEMAPHORE_EVENT)) {
			Debug.log_info(Activator.PLUGIN_ID, handlerName + ": semaphore event : " + e);
			sm.SemaphoreOp(ticket, camel,(SemaphoreEvent)e);
		}
		else {
			Debug.log_warning(Activator.PLUGIN_ID, handlerName + ": unknown event : " + e);
	    }
		
		//Debug.log_info(Activator.PLUGIN_ID, handlerName + ": leave notifyEvent");
	}

	// this method has to be synchronized to protect the use of the session ticket and event queue
	@Override
	public synchronized boolean poll() {
		//Debug.log_info(Activator.PLUGIN_ID, handlerName + ": enter poll");
		
		boolean activity = false;
		
		if(ticket == null) {
			Debug.log_warning(Activator.PLUGIN_ID, handlerName + ": ticket null. polling paused");
			thread.pausePolling();
			Debug.log_warning(Activator.PLUGIN_ID, handlerName + ": leave poll");
			return activity;
		}
		if(ticket.isLogInSuccessful() == false) {
			Debug.log_info(Activator.PLUGIN_ID, handlerName + ": not login yet. polling paused");
			thread.pausePolling();
			Debug.log_info(Activator.PLUGIN_ID, handlerName + ": leave poll");
			return activity;
		}

	
		boolean sendFailed = false;
		if(ticket.isInSession() != false) {
			if(!events.getCmdList().isEmpty()) {
				for(int i = 0; i < 3; ++i ) {
					try {
						events.setTicket(ticket);
						CommandPack pack = camel.postCommandPack(events);
						activity = true;
						ticket = pack.getTicket();
						sendFailed = false;
						events.getCmdList().clear();
						break;
					}
					catch (Exception ex) {
						ex.printStackTrace();
						sendFailed = true;
					}
				}
				if (sendFailed == true) {
					thread.pausePolling();
					Debug.log_warning(Activator.PLUGIN_ID, handlerName + ": network failure? polling paused");
				}
			}		
		}
		
		// query only if sending hasn't failed
		if (sendFailed == false) {
			try {
				boolean eventAvailable = true;
				while(eventAvailable) {
					CommandPack pack;
					if (syncFinished) {
						pack = camel.queryCommandPack(ticket, true);
					}
					else {
						pack = camel.queryCommandPack(ticket, false);
					}
					
					if ( pack.getTicket().isLastOpSuccessful()) {
						if(pack.getCmdList().isEmpty()) {
							if ( syncFinished == false ) {
								syncFinished = true;
								Debug.log_info(Activator.PLUGIN_ID, handlerName + ": sync finished");
								
								NotifySyncFinished sync = new NotifySyncFinished();
								EventManager.getInstance().dispatchEventAsynchronous(sync);
							}
							eventAvailable = false;
						}
						else {
							activity = true;
						}
					}
					else {
						Debug.log_warning(Activator.PLUGIN_ID, handlerName + ": receiving failed");
						break;
					}

					ticket = pack.getTicket();
					for ( ClientEvent recv : pack.getCmdList() ) {
						
						if(recv instanceof ClientEventChat) {
							ReceiveChatEvent event = ClientEventConvertor.convertToReceiveChatEvent((ClientEventChat)recv);
							Debug.log_info(Activator.PLUGIN_ID, handlerName + ": received chat message : " + event.getContent());
							EventManager.getInstance().dispatchEventAsynchronous(event);
						}
						else if(recv instanceof ClientEventGenericPeer) {
							ReceiveGenericPeerEvent event = ClientEventConvertor.convertToReceiveGenericPeerEvent((ClientEventGenericPeer)recv);
							Debug.log_info(Activator.PLUGIN_ID, handlerName + ": received generic message : " + event.getDataType());
							/*
							 * this is an example of how to handle the generic type event
							if ( event.getDataType().equals(String.class.getCanonicalName())) {
								String str = event.readObject(String.class);
								Debug.log_info(Activator.PLUGIN_ID, handlerName + ": received string : " + str);
							}
							*/
							EventManager.getInstance().dispatchEventAsynchronous(event);
						}
						else if(recv instanceof ClientEventSessionParticipantChange) {
							NotifySessionParticipantChange event = ClientEventConvertor.convertToNotifySessionParticipantChangeEvent(ticket,(ClientEventSessionParticipantChange)recv);
							Debug.log_info(Activator.PLUGIN_ID, handlerName + ": received ClientEventSessionParticipantChange message : " + event.isInSession());
							EventManager.getInstance().dispatchEventAsynchronous(event);
						}
						else if(recv instanceof ClientEventTeamStatusChange) {
							NotifyTeamStatusChange event = ClientEventConvertor.convertToNotifyTeamStatusChangeEvent(ticket,(ClientEventTeamStatusChange)recv);
							Debug.log_info(Activator.PLUGIN_ID, handlerName + ": received ClientEventSessionParticipantChange message : " + event.isLoggedIn());
							EventManager.getInstance().dispatchEventAsynchronous(event);
						}
						else if(recv instanceof ClientEventInvite) {
							ReceiveInvitationEvent event = ClientEventConvertor.convertToReceiveInvitationEvent(ticket,(ClientEventInvite)recv);
							Debug.log_info(Activator.PLUGIN_ID, handlerName + ": received invite message : " + event.getSessionId());
							EventManager.getInstance().dispatchEventAsynchronous(event);
						}
						else {
							Debug.log_warning(Activator.PLUGIN_ID, handlerName + ": received unknown message" );
						}
					}
				}
			}
			catch (org.jboss.remoting.CannotConnectException ex) {
				ex.printStackTrace();
				
				Debug.log_warning(Activator.PLUGIN_ID, handlerName + ": received connection exception while communicating with the server, polling paused");
				thread.pausePolling();
			}
			catch (Exception ex) {
				ex.printStackTrace();
				
				Debug.log_warning(Activator.PLUGIN_ID, handlerName + ": received exception while communicating with the server, polling paused");
				thread.pausePolling();
			}
		}
		return activity;
	}
}



